package org.snia.cdmi.locator;

import java.util.List;
import java.util.TreeMap;

import org.snia.cdmi.Constants;

public final class KetamaNodeLocator {
	
	private TreeMap<Long, Node> ketamaNodes;
	private HashAlgorithm hashAlg;
	private int numReps = 160; //默认一个节点Copy出的虚拟节点数
	private static KetamaNodeLocator locator; 		//元数据存储定位器
	private static KetamaNodeLocator cachelocator;	//缓存服务器定位器
//	private static KetamaNodeLocator metalocator;	//元数据存储定位器
	
	public KetamaNodeLocator(List<Node> nodes, HashAlgorithm alg, int nodeCopies) {
		hashAlg = alg;
		ketamaNodes = new TreeMap<Long, Node>();
		//对所有节点，生成nCopies个虚拟结点
		numReps = nodeCopies;
		for (Node node : nodes) {
			//每四个虚拟结点为一组
	        //Md5是一个16字节长度的数组，将16字节的数组每四个字节一组，  
            //分别对应一个虚拟结点，这就是为什么上面把虚拟结点四个划分一组的原因
			for (int i = 0; i < numReps / 4; i++) {
				byte[] digest = hashAlg.computeMd5(node.getHost() + i);
				for (int h = 0; h < 4; h++) {
					long m = hashAlg.hash(digest, h);
					ketamaNodes.put(m, node);
				}
			}
		}
	}

	public KetamaNodeLocator() {
	}
	
	//得到元数据定位器
	public static KetamaNodeLocator getLocator(){
		if(locator == null){
			locator = new KetamaNodeLocator(Constants
				.getListNode(), HashAlgorithm.KETAMA_HASH,
				Constants.VIRTUAL_NODE_COUNT);
		}
		return locator;
	}
	
	//得到缓存定位器
	public static KetamaNodeLocator getCacheLocator(){
		if(cachelocator == null){
			cachelocator = new KetamaNodeLocator(Constants
				.getListNode(), HashAlgorithm.KETAMA_HASH,
				Constants.VIRTUAL_NODE_COUNT);
		}
		return cachelocator;
	}
	
//	//得到元数据定位器
//	public static KetamaNodeLocator getMetaLocator(){
//		if(metalocator == null){
//			if(MODE == 0 || MODE == 1 || MODE == 2){
//				metalocator = getLocator(); 
//			}else{
//				metalocator = new KetamaNodeLocator(Constants
//					.getListNode(), HashAlgorithm.KETAMA_HASH,
//					Constants.VIRTUAL_NODE_COUNT);
//			}
//		}
//		return metalocator;
//	}
	
	public Node getPrimary(final String k) {
		byte[] digest = hashAlg.computeMd5(k);
		Node rv = getNodeForKey(hashAlg.hash(digest, 0));
		return rv;
	}

	Node getNodeForKey(long hash) {
		final Node rv;
		Long key = hash;
		if(!ketamaNodes.containsKey(key)) {
			//For JDK1.5 version
//			SortedMap<Long, Node> tailMap=ketamaNodes.tailMap(key);
//			if(tailMap.isEmpty()) {
//				key=ketamaNodes.firstKey();
//			} else {
//				key=tailMap.firstKey();
//			}
			//For JDK1.6 version
			key = ketamaNodes.ceilingKey(key);
			if (key == null) {
				key = ketamaNodes.firstKey();
			}
		}
		rv = ketamaNodes.get(key);
		return rv;
	}
}
